#include "AsyncLogging.h"

#include "LogFile.h"
#include "../Timestamp.h"

#include <cstdio>

AsyncLogging::AsyncLogging(const string &basename,
                           off_t rollSize,
                           int flushInterval)
        : flushInterval_(flushInterval),
          running_(false),
          basename_(basename),
          rollSize_(rollSize),
          thread_(std::bind(&AsyncLogging::threadFunc, this), "Logging"),
          latch_(1),
          mutex_(),
          cond_(mutex_),
        // 用于唤醒线程用
          currentBuffer_(new Buffer),
          nextBuffer_(new Buffer),
          buffers_() {
    currentBuffer_->bzero();
    nextBuffer_->bzero();
    buffers_.reserve(16);
    // 基本缓冲区数组大小为16
}

void AsyncLogging::append(const char *logline, int len) {
    MutexLockGuard lock(mutex_);
    if (currentBuffer_->avail() > len) {
        currentBuffer_->append(logline, len);
        // 有效空间长度大于当前要追加的日志长度, 就追加
    } else {
        /* 有效空间不足的处理 */
        buffers_.push_back(std::move(currentBuffer_));
        // 将当前缓冲区写入到缓冲区数组中, 然后更换当前缓冲区
        if (nextBuffer_) {
            currentBuffer_ = std::move(nextBuffer_);
        } else {
            currentBuffer_.reset(new Buffer); // Rarely happens
        }
        currentBuffer_->append(logline, len);
        cond_.notify();
        // 唤醒等待的线程判断当前是否需要处理
    }
}

void AsyncLogging::threadFunc() {
    assert(running_ == true);
    latch_.countDown();
    // 唤醒等待的线程执行任务
    LogFile output(basename_, rollSize_, false);
    BufferPtr newBuffer1(new Buffer);
    BufferPtr newBuffer2(new Buffer);
    newBuffer1->bzero();
    newBuffer2->bzero();
    // 初始化两个缓冲区, 用于写入日志, 当一个缓冲区中的大小不够用就使用nextBuffer来写入
    BufferVector buffersToWrite;
    // 待写入缓冲区
    buffersToWrite.reserve(16);
    // 申请 16 大小的内存空间

    while (running_) {
        assert(newBuffer1 && newBuffer1->length() == 0);
        assert(newBuffer2 && newBuffer2->length() == 0);
        assert(buffersToWrite.empty());

        {
            MutexLockGuard lock(mutex_);
            if (buffers_.empty())  // unusual usage!
            {
                cond_.waitForSeconds(flushInterval_);
                // 当缓冲区数组为空的情况下调用等待, 此时缓冲区中没有待写入的数据
                // 等待超过一定时间退出进行检查
            }
            buffers_.push_back(std::move(currentBuffer_));
            // 将当前写入缓冲移入数组中
            currentBuffer_ = std::move(newBuffer1);
            buffersToWrite.swap(buffers_);
            // 将待写入数据移入待写入缓冲区中
            if (!nextBuffer_) {
                nextBuffer_ = std::move(newBuffer2);
            }
        }

        assert(!buffersToWrite.empty());
        /* 处理超出缓冲区大小的情况 */
        if (buffersToWrite.size() > 25) {
            // 此时日志的写入速度大于了线程的处理能力, 将多余的丢弃处理
            char buf[256];
            snprintf(buf, sizeof buf, "Dropped log messages at %s, %zd larger buffers\n",
                     Timestamp::now().toFormattedString().c_str(),
                     buffersToWrite.size() - 2);
            fputs(buf, stderr);
            output.append(buf, static_cast<int>(strlen(buf)));
            buffersToWrite.erase(buffersToWrite.begin() + 2, buffersToWrite.end());
            // 移除写入缓冲区中的内容, 因为此时待写入缓冲区超过设定值, 会超出
        }

        for (const auto &buffer : buffersToWrite) {
            output.append(buffer->data(), buffer->length());
            // 遍历写入缓冲, 写入缓冲数组中的数据都追加到后面
        }

        if (buffersToWrite.size() > 2) {
            buffersToWrite.resize(2);
            // 删除掉没有初始化的缓冲区, 避免这些没有初始化的缓冲区被丢弃
        }

        /* 将buffersToWrite的空间再利用, 避免重复申请newBuffer的内存 */
        if (!newBuffer1) {
            assert(!buffersToWrite.empty());
            newBuffer1 = std::move(buffersToWrite.back());
            buffersToWrite.pop_back();
            newBuffer1->reset();
        }

        if (!newBuffer2) {
            assert(!buffersToWrite.empty());
            newBuffer2 = std::move(buffersToWrite.back());
            buffersToWrite.pop_back();
            newBuffer2->reset();
        }

        buffersToWrite.clear();
        // 清空待写入缓冲区

        output.flush();
        // 调用刷新策略将内容刷入
    }
    output.flush();
}